有了折線圖後,我們接下來要實做標籤篩選的功能,讓使用者可以依照標籤檢視折線圖。
沒錯,又要做 Migration!因為之前的結構,標籤是直接用 Transformable 儲存字串集合,但如果要能夠輕易篩選,我們需要將標籤獨立出來,並透過 Relationship 的方式關聯。
在 Core Data 中,要建立 Many-to-Many 關聯,必須在兩邊的 Entity 都設定 To-Many,並互相設定對方為 Destination/Inverse,否則 Core Data 不會建立 Many-To-Many 的資料關聯,也因為這樣,如果在 Migration 中,有某些收支記錄、快速記帳有關聯到相同的標籤時,Core Data 就只會幫我們建立第一個遇到的關聯,剩下後面重複的都會消失,身為專業工程師,讓使用者的資料消失是會影響職業生涯,非常嚴重的問題。因此在處理資料時,需格外小心。
前面在轉換收支記錄統計時,有實做過 NSEntityMigrationPolicy,但這次比較不一樣的是,需要我們特別處理的部分,是「關聯」的部分,因此這次我們必須提供 createRelationships(forDestination:in:manager) 方法,協助 Core Data 處理資料中間的關聯。
在 Core Data - The Migration Process 中,Core Data 有簡單說明 Migration 的三個步驟:
延續上述,我們這次專心處理標籤的關聯,因此我們只實做第二階段「重建關聯」相關邏輯。
邏輯順序大概是:
Set<String>
)程式碼如下:
let sTransaction = manager.sourceInstances(forEntityMappingName: mapping.name, destinationInstances: [dInstance]).first!
let tagNames = sTransaction.value(forKey: "tags") as! Set<String>
var tags = Set<NSManagedObject>()
tagNames.forEach { tagName in
tags.insert(createDestinationTag(named: tagName, manager: manager))
}
dInstance.setValue(tags, forKey: "tags")
private func createDestinationTag(named tagName: String, manager: NSMigrationManager) -> NSManagedObject {
let request = NSFetchRequest<NSManagedObject>(entityName: String(describing: Tag.self))
request.predicate = NSPredicate(format: "\(#keyPath(Tag.name)) = %@", argumentArray: [tagName])
let tags = try! manager.destinationContext.fetch(request)
if tags.isEmpty {
let tag = NSEntityDescription.insertNewObject(forEntityName: String(describing: Tag.self), into: manager.destinationContext)
tag.setValue(tagName, forKey: "name")
return tag
}
return tags.first!
}
使用者點選標籤後,會即時更新折線圖的資料,流程如下:
用 NSPredicate,語法可以參照這篇文章,邏輯是只要 TransactionStats 的標籤中,有包含使用者選到的標籤都要被抓出來,如下:
fetchedResultsController.fetchRequest.predicate = NSPredicate(format: "ANY \(#keyPath(TransactionStats.tags)) IN %@", argumentArray: [selectedTags])
try! fetchedResultsController.performFetch()
程式碼:GitHub
主要想做的功能大概有做到,接下來要把 Issue 解一解,然後整理下程式,送審囉!